package xgenerator.jdbc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Configuration {
	
	/**
	 * 默认的数据源名
	 */
	public static final String DEFAULT_DS_NAME = "default";
	
	/**
	 * 默认的jdbc配置文件
	 */
	private static final String DEFAULT_JDBC_CONFIG_FILE = "jdbc.properties";
	
	/**
	 * 默认的DataSource配置文件
	 */
	private static final String DEFAULT_DATA_SOURCE_CONFIG_FILE = "jdbc.cfg.xml";
	
	/**
	 * 默认的jdbc配置文件位置
	 */
	private static final String DEFAULT_JDBC_CONFIG_FILE_LOCATION = System.getProperty("user.dir") + File.separator + "config" + File.separator + DEFAULT_JDBC_CONFIG_FILE;
	
	/**
	 * 默认的DataSource配置文件位置
	 */
	private static final String DEFAULT_DATA_SOURCE_CONFIG_FILE_LOCATION = System.getProperty("user.dir") + File.separator + "config" + File.separator + DEFAULT_DATA_SOURCE_CONFIG_FILE;
	
	/**
	 * defaultDataSourceName
	 */
	private String defaultDataSourceName;
	
	/**
	 * DataSource Properties配置
	 */
	private Map<String, Properties> propertiesMap;
	
	/**
	 * constructor
	 */
	public Configuration() {
		this.propertiesMap = new HashMap<String, Properties>();
	}
	
	/**
	 * 
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public Configuration configure() {
//		configure(DEFAULT_JDBC_CONFIG_FILE_LOCATION);
		configure(DEFAULT_DATA_SOURCE_CONFIG_FILE_LOCATION);
		return this;
	}
	
	/**
	 * 
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param configFile
	 * @return
	 */
	public Configuration configure(String configFile) {
		configure(new File(configFile));
		return this;
	}
	
	/**
	 * 
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param configFile
	 * @return
	 */
	public Configuration configure(File configFile) {
		try {
			configure(new FileInputStream(configFile));
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
		return this;
	}
	
	/**
	 * 
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param inStream
	 * @return
	 */
	public Configuration configure(InputStream inStream) {
		try {
			DataSourcesXmlConfig dataSourcesXmlConfig = new DataSourcesXmlConfig(inStream);
			
			this.defaultDataSourceName = dataSourcesXmlConfig.getDefaultDataSourceName();
			
			Vector<String> dataSourceNames = dataSourcesXmlConfig.getDataSourceNames();
			for(int i=0; i<dataSourceNames.size(); i++) {
				String oneDataSourceName = dataSourceNames.get(i);
				
				Properties properties = new Properties();

				properties.setProperty("name", oneDataSourceName);
				properties.setProperty("jdbc.driverClassName", dataSourcesXmlConfig.getDriverClassName(oneDataSourceName));
				properties.setProperty("jdbc.url", dataSourcesXmlConfig.getUrl(oneDataSourceName));
				properties.setProperty("jdbc.username", dataSourcesXmlConfig.getUsername(oneDataSourceName));
				properties.setProperty("jdbc.password", dataSourcesXmlConfig.getPassword(oneDataSourceName));
				
				this.propertiesMap.put(oneDataSourceName, properties);
			}
		} catch (Exception e) {
			Properties properties = new Properties();
			try {
				properties.load(new InputStreamReader(inStream));
			} catch (IOException e1) {
				throw new RuntimeException(e);
			}
			addDataSourceProperties(DEFAULT_DS_NAME, properties);
		}
		
		return this;
	}
	
	/**
	 * addDataSourceProperties
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param dataSourceName
	 * @param properties
	 */
	public void addDataSourceProperties(String dataSourceName, Properties properties) {
		this.propertiesMap.put(dataSourceName, properties);
	}

	/**
	 * buildDataSourceFactory
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public DataSourceFactory buildDataSourceFactory() {
		return new DataSourceFactory(this.propertiesMap, this.defaultDataSourceName);
	}

	/**
	 * buildDataSourceFactory
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param isContainerDataSource 是否为容器数据源
	 * @return
	 */
	public DataSourceFactory buildDataSourceFactory(boolean isContainerDataSource) {
		return new DataSourceFactory(this.propertiesMap, this.defaultDataSourceName, isContainerDataSource);
	}

	/**
	 * <p>
	 * Title：DataSourcesXmlConfig
	 * </p>
	 * <p>
	 * Description：
	 * </p>
	 * <p>
	 * Copyright: Copyright (c) 2012
	 * </p>
	 * <p>
	 * Company: liss
	 * </p>
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @version 1.0
	 * @see 
	 * @since 1.0
	 */
	public class DataSourcesXmlConfig {
		
		/**
		 * XPathFactory
		 */
		private XPathFactory factory = XPathFactory.newInstance();
		
		/**
		 * XPath
		 */
		private XPath xpath = factory.newXPath();
		
		/**
		 * 
		 */
		private Document doc;
		
		/**
		 * constructor
		 * @param xmlFile
		 * @throws FileNotFoundException 
		 */
		public DataSourcesXmlConfig(String xmlFile) throws FileNotFoundException {
			this(new File(xmlFile));
		}
		
		/**
		 * constructor
		 * @param xmlFile
		 * @throws FileNotFoundException
		 */
		public DataSourcesXmlConfig(File xmlFile) throws FileNotFoundException {
			this(new FileInputStream(xmlFile));
		}
		
		/**
		 * constructor
		 * @param is
		 */
		public DataSourcesXmlConfig(InputStream is) {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true); // never forget this!
			try {
				DocumentBuilder builder = factory.newDocumentBuilder();
				this.doc = builder.parse(is);
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
			} catch (SAXException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		/**
		 * 获取默认数据源名称
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @return
		 */
		public String getDefaultDataSourceName() {
			String defaultDataSourceName = null;
			
			NodeList nodeList = this.doc.getElementsByTagName("datasources");  //所有nodeName节点的列表
			if(null == nodeList || 0 == nodeList.getLength()) {
				return DEFAULT_DS_NAME;
			}

			Node oneNode = nodeList.item(0);
			NamedNodeMap nodeAttrMap = oneNode.getAttributes();  //节点nodeName的所有属性的map
			Node attr = nodeAttrMap.getNamedItem("default");  //一个节点属性相当于节点的一个子节点
			defaultDataSourceName = attr.getNodeValue();
			
			return defaultDataSourceName;
		}
		
		/**
		 * 
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @return
		 */
		public Vector<String> getDataSourceNames() {
			Vector<String> dataSourceNames = new Vector<String>();
			
			NodeList nodeList = this.doc.getElementsByTagName("datasource");  //所有nodeName节点的列表
			if(null == nodeList) {
				return dataSourceNames;
			}
			for(int i=0; i<nodeList.getLength(); i++) {
				Node oneNode = nodeList.item(i);
				NamedNodeMap nodeAttrMap = oneNode.getAttributes();  //节点nodeName的所有属性的map
				Node attr = nodeAttrMap.getNamedItem("name");  //一个节点属性相当于节点的一个子节点
				dataSourceNames.add(attr.getNodeValue());
			}
			
			return dataSourceNames;
		}
		
		/**
		 * 
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @param query
		 * @return
		 */
		private String find(String query) {
			String result = null;
			
			try {
				XPathExpression expr = xpath.compile(query);
				result = expr.evaluate(this.doc);
			} catch (XPathExpressionException e) {
				throw new RuntimeException(e);
			}
			
			return result;
		}
		
		public String getDriverClassName(String dataSourceName) {
			return find("//datasource[@name=\"" + dataSourceName + "\"]/driverClassName/text()");
		}
		
		public String getUrl(String dataSourceName) {
			return find("//datasource[@name=\"" + dataSourceName + "\"]/url/text()");
		}
		
		public String getUsername(String dataSourceName) {
			return find("//datasource[@name=\"" + dataSourceName + "\"]/username/text()");
		}
		
		public String getPassword(String dataSourceName) {
			return find("//datasource[@name=\"" + dataSourceName + "\"]/password/text()");
		}
	}//end class DataSourcesXmlConfig

}
